package com.organization

import static org.quartz.JobBuilder.*;
import static org.quartz.TriggerBuilder.*;
import static org.quartz.DateBuilder.futureDate
import org.quartz.Scheduler
import org.quartz.impl.StdSchedulerFactory
import org.quartz.JobDetail
import org.quartz.SimpleTrigger
import org.quartz.TriggerKey
import org.quartz.DateBuilder
import java.util.regex.Pattern;

class LocalDataService {

    def dataService
    def springcacheService

    private static final def failResponse = [success:false,error:"Remote service is not available!"]
    private static final String cacheElementPrefix = "elementCache"
    private static final String cacheTreePrefix = "treeCache"
    private static Scheduler scheduler = new StdSchedulerFactory().getScheduler();

    def getOrganizationInfo(){
        //get organization info from cache
        springcacheService.doWithCache("titleCache",1,{->
            dataService.getOrganizationInfo()
        })
    }

    def getBranch(String nodeId){
        //get branch from cache. any of crud operations clear all tree caches
        springcacheService.doWithCache(cacheTreePrefix+nodeId,cacheTreePrefix+nodeId,{->
            println "update tree in cache for node "+nodeId
            scheduleCacheFlush(cacheTreePrefix+nodeId,nodeId)
            def result = [:]
            try {
                result = dataService.getBranch(nodeId)
            }
            catch (RuntimeException e){
                result.putAll(failResponse)
            }
            return result
        })
    }

    def getElement(String nodeId){
        //get element info from cache
        springcacheService.doWithCache(cacheElementPrefix+nodeId,cacheElementPrefix+nodeId,{->
            println "update element in cache for node "+nodeId
            scheduleCacheFlush(cacheElementPrefix+nodeId,nodeId)
            def result = [:]
            try {
                result = dataService.getElement(nodeId)
            }
            catch (RuntimeException e){
                result.putAll(failResponse)
            }
            return result
        })
    }

    def updateElement(String nodeId, Map params){
        flushTreeCache("*")
        flushElementCache(nodeId)

        def result = [:]
        try {
            result = dataService.updateElement(params)
        }
        catch (RuntimeException e){
            result.putAll(failResponse)
        }
        return result
    }

    def addElement(String parentNodeId, Map params){
        flushTreeCache("*")

        def result = [:]
        try {
            result = dataService.addElement(params)
        }
        catch (RuntimeException e){
            result.putAll(failResponse)
        }
        return result
    }

    def deleteElement(String nodeId, Map params){
        flushTreeCache("*")
        flushElementCache(nodeId)

        def result = [:]
        try {
            result = dataService.deleteElement(params)
        }
        catch (RuntimeException e){
            result.putAll(failResponse)
        }
        return result
    }

    private void scheduleCacheFlush(String cacheName,String nodeId) throws Exception{
        //unschedule previos job for current key
        scheduler.unscheduleJob(TriggerKey.triggerKey(cacheName))

        //schedule job for flushing cache with name cacheName
        JobDetail job = newJob(CacheFlushJob.class)
                .withIdentity(cacheName)
                .build();

        SimpleTrigger trigger = (SimpleTrigger) newTrigger()
                .withIdentity(TriggerKey.triggerKey(cacheName))
                .usingJobData("cacheName",cacheName)
                .usingJobData("nodeId",nodeId)
                .startAt(futureDate(600,DateBuilder.IntervalUnit.SECOND))
                .forJob(job)
                .build();

        scheduler.scheduleJob(job, trigger);
        if (!scheduler.isStarted())
            scheduler.start()
    }

    //remove quartz job for trigger key
    private void removeJobWithTriggerKey(String key){
        scheduler.unscheduleJob(TriggerKey.triggerKey(key));
    }

    /*----------------------------------------
    --Next methods will be flush some cached values
    --This methods called, then messages from JGroups arrived
    --Also this methods used, then CRUD operations execute
    --------------------------------------  */
    //flush element cache
    def flushElementCache(String nodeId){
        String cacheName = cacheElementPrefix+nodeId
        println "flush cache for "+cacheName
        springcacheService.flush(cacheName)
        removeJobWithTriggerKey(cacheName)
    }

    //flush all tree caches
    def flushTreeCache(String nodeId){
        String cacheName = cacheTreePrefix+nodeId
        println "flush cache for "+cacheName
        if (nodeId=="*") {
            springcacheService.flush(Pattern.compile('^'+cacheTreePrefix+'.*$'))
        }
        else{
            springcacheService.flush(cacheName)
            removeJobWithTriggerKey(cacheName)
        }
    }
}
